#pragma once

#include "Utils.h"
#include "Texture.h"
#include "Material.h"
#include "Objects.h"

#include <qstring.h>
#include <qfile.h>
#include <qjsonarray.h>
#include <qjsonobject.h>
#include <qjsondocument.h>

class FileManager
{
	QFile file;
public:
	FileManager(QString name) : file(name) {}
	inline QJsonArray vec2Json(vec item)
	{
		return QJsonArray({ item.x(),item.y(),item.z() });
	}
	inline vec Json2vec(QJsonValue item)
	{
		return vec(item.toArray().at(0).toDouble(), item.toArray().at(1).toDouble(), item.toArray().at(2).toDouble());
	}
	inline QJsonObject pur2Json(pureColor* pur,int id)
	{
		return QJsonObject({ {"type",0},{"id",id},{"name",pur->name},{"color",vec2Json(pur->color)}});
	}
	inline pureColor* Json2pur(QJsonObject obj)
	{
		return new pureColor(obj.value("name").toString(), Json2vec(obj.value("color")));

	}
	inline QJsonObject ckb2Json(checkBox* ckb, int id)
	{
		return QJsonObject({ {"type",1},{"id",id},{"name",ckb->name},{"color",vec2Json(ckb->color)},{"darkColor",vec2Json(ckb->darkColor)},{"dir",ckb->dir},{"contrast",ckb->contrast},{"scale",vec2Json(ckb->scale)}});
	}
	inline checkBox* Json2ckb(QJsonObject obj)
	{
		return new checkBox(obj.value("name").toString(), Json2vec(obj.value("color")), Json2vec(obj.value("darkColor")), Json2vec(obj.value("scale")), obj.value("dir").toInt(), obj.value("contrast").toBool());
	}
	inline QJsonArray GenerateTexArray()
	{
		QJsonArray texArray;
		for (int i = 0; i < texList.count(); i++)
		{
			switch (texList[i]->type)
			{
			case texType::PUR:
				texArray.append(pur2Json((pureColor*)texList[i], i));
				break;
			case texType::CKB:
				texArray.append(ckb2Json((checkBox*)texList[i], i));
				break;
			}
		}
		return texArray;
	}
	inline void unserializeTexArray(QJsonArray texArray)
	{
		texList.clear();
		for (auto value : texArray)
		{
			if (!value.isObject()) continue;
			QJsonObject obj = value.toObject();
			switch (obj.value("type").toInt())
			{
			case 0:
				texList.append(Json2pur(obj));
				break;
			case 1:
				texList.append(Json2ckb(obj));
				break;
			}
		}
	}
	inline QJsonObject dfs2Json(Diffuse* dfs, int id)
	{
		return QJsonObject({ {"type",0}, {"name",dfs->name},{"id",id},{"tex",texList.indexOf(dfs->color)}});
	}
	inline Diffuse* Json2dfs(QJsonObject obj)
	{
		return new Diffuse(obj.value("name").toString(), texList[obj.value("tex").toInt()]);
	}
	inline QJsonObject mir2Json(Mirror* mir, int id)
	{
		return QJsonObject({ {"type",1}, {"name",mir->name},{"id",id}});
	}
	inline Mirror* Json2mir(QJsonObject obj)
	{
		return new Mirror(obj.value("name").toString());
	}
	inline QJsonObject mtl2Json(Metal* mtl, int id)
	{
		return QJsonObject({ {"type",2},{"id",id},{"name",mtl->name},{"tex",texList.indexOf(mtl->color)},{"rough",mtl->roughness} });
	}
	inline Metal* Json2mtl(QJsonObject obj)
	{
		return new Metal(obj.value("name").toString(), texList[obj.value("tex").toInt()], obj.value("rough").toDouble());
	}
	inline QJsonObject flt2Json(Flat* flt, int id)
	{
		return QJsonObject({ {"type",3}, {"name",flt->name},{"id",id},{"tex",texList.indexOf(flt->color)} });
	}
	inline Flat* Json2flt(QJsonObject obj)
	{
		return new Flat(obj.value("name").toString(), texList[obj.value("tex").toInt()]);
	}
	inline QJsonObject emt2Json(Emission* emt, int id)
	{
		return QJsonObject({ {"type",4},{"name",emt->name},{"id",id},{"tex",texList.indexOf(emt->color)},{"scale",emt->scale} });
	}
	inline Emission* Json2emt(QJsonObject obj)
	{
		return new Emission(obj.value("name").toString(), texList[obj.value("tex").toInt()], obj.value("scale").toDouble());
	}
	inline QJsonArray GenerateMatArray()
	{
		QJsonArray matArray;
		for (int i = 0; i < matList.count(); i++)
		{
			switch (matList[i]->type)
			{
			case materialType::DFS:
				matArray.append(dfs2Json((Diffuse*)matList[i], i));
				break;
			case materialType::MIR:
				matArray.append(mir2Json((Mirror*)matList[i], i));
				break;
			case materialType::MTL:
				matArray.append(mtl2Json((Metal*)matList[i], i));
				break;
			case materialType::FLT:
				matArray.append(flt2Json((Flat*)matList[i], i));
				break;
			case materialType::EMT:
				matArray.append(emt2Json((Emission*)matList[i], i));
				break;
			}
		}
		return matArray;
	}
	inline void unserializeMatArray(QJsonArray matArray)
	{
		matList.clear();
		for (auto value : matArray)
		{
			if (!value.isObject()) continue;
			QJsonObject obj = value.toObject();
			switch (obj.value("type").toInt())
			{
			case 0:
				matList.append(Json2dfs(obj));
				break;
			case 1:
				matList.append(Json2mir(obj));
				break;
			case 2:
				matList.append(Json2mtl(obj));
				break;
			case 3:
				matList.append(Json2flt(obj));
				break;
			case 4:
				matList.append(Json2emt(obj));
				break;
			}
		}
	}
	inline QJsonObject sph2Json(Sphere* sph)
	{
		return QJsonObject({ {"type",0}, {"name",sph->name},{"cen",vec2Json(sph->cen)},{"rad",sph->rad},{"mat",matList.indexOf(sph->mat)} });
	}
	inline Sphere* Json2sph(QJsonObject obj)
	{
		return new Sphere(obj.value("name").toString(), Json2vec(obj.value("cen")), obj.value("rad").toDouble(), matList[obj.value("mat").toInt()]);
	}
	inline QJsonArray GenerateObjArray()
	{
		QJsonArray objArray;
		for (int i = 0; i < mainWorld.objlist.count(); i++)
		{
			switch (mainWorld.objlist[i]->type)
			{
			case objType::Sph:
				objArray.append(sph2Json((Sphere*)mainWorld.objlist[i]));
				break;
			}
		}
		return objArray;
	}
	inline void unserializeObjArray(QJsonArray objArray)
	{
		mainWorld.objlist.clear();
		for (auto value : objArray)
		{
			if (!value.isObject()) continue;
			QJsonObject obj = value.toObject();
			switch (obj.value("type").toInt())
			{
			case 0:
				mainWorld.objlist.append(Json2sph(obj));
				break;
			}
		}
	}
	inline QJsonObject cam2Json()
	{
		return QJsonObject({ {"pos",vec2Json(mainWorld.cam.org)},{"tar",vec2Json(mainWorld.cam.target)},{"fov",mainWorld.cam.fov} });
	}
	inline void Json2cam(QJsonObject obj)
	{
		mainWorld.cam.org = Json2vec(obj.value("pos"));
		mainWorld.cam.target = Json2vec(obj.value("tar"));
		mainWorld.cam.fov = obj.value("fov").toDouble();
	}
	void Save()
	{
		if (!file.open(QIODevice::WriteOnly)) return;
		file.write(QJsonDocument(QJsonObject({ {"cam",cam2Json()},{"tex",GenerateTexArray()},{"mat",GenerateMatArray()},{"obj",GenerateObjArray()}})).toJson());
	}
	void Open()
	{
		if (!file.open(QIODevice::ReadOnly)) return;
		QJsonObject root = QJsonDocument::fromJson(file.readAll()).object();
		unserializeTexArray(root.value("tex").toArray());
		unserializeMatArray(root.value("mat").toArray());
		unserializeObjArray(root.value("obj").toArray());
		Json2cam(root.value("cam").toObject());
	}
};

